package com.yc.sole.framework.redis;

import com.yc.sole.framework.exception.BaseException;
import com.yc.sole.framework.utils.IpUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author yizuomin
 * @date 2023/5/16 14:41
 **/
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AccessLimitAspect {

    private final RedissonClient redissonClient;

    @Around("@annotation(com.yc.sole.framework.redis.AccessLimit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature msg = (MethodSignature) signature;
        Object target = point.getTarget();
        Method method = target.getClass().getMethod(msg.getName(), msg.getParameterTypes());
        //通过方法获取注解
        AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        if (Objects.isNull(servletRequestAttributes)) {
            throw new BaseException("无法获取请求上下文");
        }
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String ipAddress = IpUtil.getIpAddress(request);
        String key = "access:limiter:" + method.getName() + ":" + ipAddress;

        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        rateLimiter.setRate(RateType.OVERALL, accessLimit.limit(), accessLimit.seconds(), RateIntervalUnit.SECONDS);
        if (!rateLimiter.tryAcquire()) {
            throw new BaseException("访问次数超限");
        }
        return point.proceed();
    }
}
